// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 内存统计信息

package runtime

import (
	"runtime/internal/atomic"
	"runtime/internal/sys"
	"unsafe"
)

// 统计信息。
// 
// 有关详细说明，请参阅MemStats文档。
// 与MemStats不同的字段将在此处进一步记录。
// 
// 这些字段中的许多字段是动态更新的，而其他字段只有
// 在调用updateemstats时更新。
type mstats struct {
	// 一般统计数据。
	alloc       uint64 // 已分配和尚未释放的字节
	total_alloc uint64 // 已分配的字节（即使已释放）
	sys         uint64 // 从系统中获取的字节（应为以下xxx_sys的总和，无锁定，近似值）
	nlookup     uint64 // 指针查找数（未使用）
	nmalloc     uint64 // malloc的数量
	nfree       uint64 // 空闲的数量

	// malloc堆的统计信息。
	// 以原子方式更新，或者随着世界停止而更新。
	// 
	// 与MemStats一样，heap\u sys和heap\u inuse不计算手动管理范围内的内存
	// 。
	heap_sys      sysMemStat // 从系统中为GC的堆获取的虚拟地址空间
	heap_inuse    uint64     // mSpanInUse中的字节跨越
	heap_released uint64     // 释放到操作系统的字节

	// 运行时不直接使用堆对象，而是由UPDATEMSTATS动态计算
	// 。
	heap_objects uint64 // 已分配对象的总数

	// 有关堆栈的统计信息。
	stacks_inuse uint64     // 手动管理堆栈跨距中的字节；UPDATEMMSTATS 
	stacks_sys   sysMemStat // /计算得出的值仅统计mstats中的newosproc0堆栈；与MemStats.StackSys 

	// 有关低级固定大小结构分配的统计信息不同。
	// 由FixAlloc锁保护。
	mspan_inuse  uint64 // mspan结构
	mspan_sys    sysMemStat
	mcache_inuse uint64 // mcache结构
	mcache_sys   sysMemStat
	buckhash_sys sysMemStat // 评测桶哈希表

	// 有关GC开销的统计信息。
	gcWorkBufInUse           uint64     // 由UPDATEMSTATS计算
	gcProgPtrScalarBitsInUse uint64     // 由UPDATEMSTATS计算
	gcMiscSys                sysMemStat // 以原子方式或在STW期间更新

	// 杂项统计信息。
	other_sys sysMemStat // 以原子方式或在STW期间更新

	// 有关垃圾收集器的统计信息。

	// 受mheap保护或在GC期间停止世界。
	last_gc_unix    uint64 // 上次gc（unix时间）
	pause_total_ns  uint64
	pause_ns        [256]uint64 // 最近gc暂停长度的循环缓冲区
	pause_end       [256]uint64 // 最近gc结束时间的循环缓冲区（自1970年以来的纳秒）
	numgc           uint32
	numforcedgc     uint32  // 强制GCs的用户数
	gc_cpu_fraction float64 // GC使用的CPU时间分数
	enablegc        bool
	debuggc         bool

	// 有关分配大小类的统计信息。

	by_size [_NumSizeClasses]struct {
		size    uint32
		nmalloc uint64
		nfree   uint64
	}

	// 为偶数个大小类添加一个uint32，以便将以下字段对齐
	// 为32位平台上的原子操作添加64位。
	_ [1 - _NumSizeClasses%2]uint32

	last_gc_nanotime uint64 // last gc（单调时间）
	last_heap_inuse  uint64 // heap\u在上一个gc终止时使用

	// heapStats是一组统计数据
	heapStats consistentHeapStats

	// uint32 

	// GCPauseList表示所有与gc相关的
	// 应用程序在运行时暂停的分布。
	// 
	// 与暂停不同，每个暂停都是单独计算的。
	gcPauseDist timeHistogram
}

var memstats mstats

// MemStats记录有关内存分配器的统计信息。
type MemStats struct {
	// 一般统计数据。

	// Alloc是已分配堆对象的字节数。
	// 
	// 这与HeapAlloc相同（见下文）。
	Alloc uint64

	// TotalAlloc是为堆对象分配的累积字节数。
	// 
	// TotalAlloc随着堆对象的分配而增加，但
	// 与Alloc和HeapAlloc不同，释放
	// 对象时，TotalAlloc不会减少。
	TotalAlloc uint64

	// Sys是从操作系统获得的内存总字节数。
	// 
	// Sys是以下XSys字段的总和。Sys测量Go运行时为
	// 虚拟地址空间。很可能不是所有的虚拟地址空间在任何给定的时刻都由物理内存支持，尽管总体上是在某个时刻。
	// 堆、堆栈和其他内部数据结构保留的
	Sys uint64

	// Lookups是
	// 运行时执行的指针查找数。
	// 
	// 这主要用于调试运行时内部。
	Lookups uint64

	// Mallocs是分配的堆对象的累积计数。
	// 活动对象的数量为Mallocs-Frees。
	Mallocs uint64

	// Frees是已释放的堆对象的累积计数。
	Frees uint64

	// 堆内存统计信息。
	// 
	// 解释堆统计信息需要一些关于
	// 如何组织内存的知识。Go将堆的虚拟地址
	// 空间划分为“范围”，即内存8K或更大的连续
	// 区域。span可能处于三种状态之一：
	// 
	// 
	// 空闲span不包含对象或其他数据。
	// 支持空闲范围的物理内存可以释放回操作系统（但虚拟地址空间永远不会释放），也可以转换为“正在使用”或“堆栈”范围。
	// 
	// 一个“正在使用”的范围至少包含一个堆对象，并且可能有可用空间分配更多堆对象。
	// 
	// “堆栈”跨度用于goroutine堆栈。堆栈范围
	// 不被视为堆的一部分。span可以在堆和堆栈内存之间更改
	// ；绝对不能同时用于
	// 和

	// HeapAlloc是已分配堆对象的字节数。
	// 
	// “已分配”堆对象包括所有可访问对象，如
	// 以及垃圾收集器尚未释放的不可访问对象
	// 等。具体来说，HeapAlloc随着堆
	// 对象的分配而增加，随着堆的扫描
	// 和不可访问对象的释放而减少。清扫在GC循环之间以增量方式发生，因此这两个过程同时发生，因此HeapAlloc趋向于平稳地改变（与停止世界垃圾收集器的典型锯齿形相反）。
	HeapAlloc uint64

	// HeapSys是从操作系统获得的堆内存字节数。
	// 
	// HeapSys测量为堆保留的虚拟地址空间量
	// 。这包括已保留但尚未使用的虚拟地址空间
	// 它不消耗
	// 物理内存，但往往很小，以及虚拟
	// 物理内存已被
	// 闲置后返回操作系统的地址空间（参见heapprereleased 
	// 了解后者的度量值）。
	// 
	// HeapSys估计堆的最大大小。
	HeapSys uint64

	// HeapIdle是空闲字节（未使用）span.
	// 
	// 空闲span中没有对象。这些span可能是
	// （可能已经是）返回到操作系统，或者它们可以被重新用于堆分配，或者它们可以被重新用作堆栈内存
	HeapIdle uint64

	// HeapInuse是使用中的字节数。
	// 
	// 正在使用的跨区中至少有一个对象。这些跨区
	// 只能用于大小大致相同的其他对象。
	// 大小。
	// 
	// HeapInuse减去HeapAlloc估计已专用于p的内存量
	// 特定大小的类，但当前未使用。这是
	// 碎片的上限，但通常可以有效地重用此内存
	// 
	HeapInuse uint64

	// HeapReleased是返回到操作系统的物理内存字节。
	// 
	// 此从已返回到操作系统且尚未为堆重新获取的空闲范围中计算堆内存。
	HeapReleased uint64

	// HeapObjects是已分配的堆对象数。
	// 
	// 与HeapAlloc一样，这会随着对象的分配而增加，而
	// 随着堆被扫描，无法访问的对象被释放。堆栈内存统计。堆栈不被认为是堆的一部分，但运行时可以将一段堆内存用于堆栈内存，反之亦然。
	HeapObjects uint64


	// StackInuse是堆栈跨度中的字节。
	// 
	// 正在使用的堆栈范围中至少有一个堆栈。这些
	// 跨距只能用于相同大小的其他堆栈。
	// 
	// 没有堆栈空闲，因为未使用的堆栈跨度返回到堆中（因此计入HeapIdle）。
	StackInuse uint64

	// StackSys是从操作系统获得的堆栈内存字节数。
	// 
	// StackSys已使用，加上直接从OS for OS线程堆栈获取的任何内存
	// （应最小）。
	StackSys uint64

	// 堆外内存统计信息。
	// 
	// 以下统计信息度量运行时内部
	// 未从堆内存分配的结构（通常是
	// 因为它们是实现堆的一部分）。与
	// 堆或堆栈内存不同，分配给这些
	// 结构的任何内存都专用于这些结构。
	// 
	// 这些主要用于调试运行时内存
	// 开销。

	// MSpanInuse是已分配的mspan结构的字节数。
	MSpanInuse uint64

	// MSpanSys是从操作系统获取的用于mspan 
	// 结构的内存字节。
	MSpanSys uint64

	// MCacheInuse是已分配的mcache结构的字节。
	MCacheInuse uint64

	// MCacheSys是从操作系统获取的用于
	// mcache结构的内存字节。
	MCacheSys uint64

	// BuckHashSys是分析桶哈希表中的内存字节。
	BuckHashSys uint64

	// GCSys是垃圾收集元数据中的内存字节。
	GCSys uint64

	// OtherSys是其他堆外内存的字节数
	// 运行时分配。
	OtherSys uint64

	// 垃圾收集器统计信息。

	// NextGC是下一个GC循环的目标堆大小。垃圾收集器的目标是保持HeapAlloc≤ 下一步。
	// 在每个GC循环结束时，根据可访问数据量和GOGC的
	// 值。
	// 值计算下一个循环的目标
	NextGC uint64

	// LastGC是自1970年（UNIX时代）以来最后一次垃圾收集完成的时间，与
	// 纳秒一样。
	LastGC uint64

	// PauseTalons是GC中的累积纳秒
	// 停止程序启动后的世界暂停。
	// 
	// 在停止世界暂停期间，所有goroutine都暂停
	// 并且只有垃圾收集器可以运行。
	PauseTotalNs uint64

	// PauseNs是最近全球GC stop的循环缓冲区
	// 以纳秒为单位的暂停时间。
	// 
	// 最近一次暂停在PauseNs[（NumGC+255）%256]。在
	// 概述中，PauseNs[N%256]记录最近N%256次GC循环中最
	// 暂停的时间。每个
	// GC循环可能有多个暂停；这是一个循环中所有暂停的总和。
	PauseNs [256]uint64

	// PauseEnd是一个循环缓冲区，自1970年（UNIX时代）以来，它是最近GC暂停结束时间的纳秒级。
	// 
	// 此缓冲区的填充方式与PauseNs相同。每个GC循环可能有多个暂停；这记录了
	// 循环中最后一次暂停的结束。
	PauseEnd [256]uint64

	// NumGC是完成的GC循环数。
	NumGC uint32

	// NumForcedGC是由调用GC函数的应用程序
	// 强制执行的GC循环数。
	NumForcedGC uint32

	// GCCPUFraction是自程序启动以来GC使用的该程序可用
	// CPU时间的一部分。
	// 
	// GCCPUFraction表示为一个介于0和1之间的数字，其中0表示GC未消耗此程序的任何CPU。
	// 程序的可用CPU时间定义为自程序启动以来
	// GOMAXPROCS的积分。也就是说，如果
	// GOMAXPROCS是2，并且一个程序已经运行了10秒，那么它的“可用CPU”是20秒。GCCPUFraction 
	// 不包括用于写屏障活动的CPU时间。
	// 
	// 这与
	// GODEBUG=gctrace=1报告的CPU分数相同。
	GCCPUFraction float64

	// EnableGC表示GC已启用。这总是正确的，即使GOGC=off。
	EnableGC bool

	// 调试GC当前未使用。
	DebugGC bool

	// 按大小报告每种大小的类分配统计信息。
	// 
	// BySize[N]提供大小为S的分配的统计信息，其中
	// 按大小[N-1]。大小<S≤ 按大小[N]。大小。
	// 
	// 这不报告大于BySize[60]的分配。大小。
	BySize [61]struct {
		// Size是此
		// Size类中对象的最大字节大小。
		Size uint32

		// Mallocs是在此大小类中分配的堆对象的累积计数
		// 。分配的累积字节
		// 为Size*Mallocs。此大小类中的活动
		// 对象数为Mallocs-Frees。
		Mallocs uint64

		// Frees是此大小类中已释放的堆对象的累积计数。
		Frees uint64
	}
}

func init() {
	if offset := unsafe.Offsetof(memstats.heapStats); offset%8 != 0 {
		println(offset)
		throw("memstats.heapStats not aligned to 8 bytes")
	}
	if offset := unsafe.Offsetof(memstats.gcPauseDist); offset%8 != 0 {
		println(offset)
		throw("memstats.gcPauseDist not aligned to 8 bytes")
	}
	// 确保heapStatsDelta的大小导致相邻字段/插槽（例如
	// /[3]heapStatsDelta）对齐8字节。
	if size := unsafe.Sizeof(heapStatsDelta{}); size%8 != 0 {
		println(size)
		throw("heapStatsDelta not a multiple of 8 bytes in size")
	}
}

// ReadMemStats使用内存分配器统计信息填充m。
// 
// 返回的内存分配器统计信息在调用ReadMemStats时是最新的。这与堆配置文件
// 形成对比，它是最近完成的垃圾
// 收集周期的快照。
func ReadMemStats(m *MemStats) {
	stopTheWorld("read mem stats")

	systemstack(func() {
		readmemstats_m(m)
	})

	startTheWorld()
}

func readmemstats_m(stats *MemStats) {
	updatememstats()

	stats.Alloc = memstats.alloc
	stats.TotalAlloc = memstats.total_alloc
	stats.Sys = memstats.sys
	stats.Mallocs = memstats.nmalloc
	stats.Frees = memstats.nfree
	stats.HeapAlloc = memstats.alloc
	stats.HeapSys = memstats.heap_sys.load()
	// 根据定义，HeapIdle是为堆映射的
	// 但当前未用于保存堆
	// 对象的内存。它也特别是可以用于其他目的的内存，比如堆栈，但是在进行转换之前，这个内存从HeapSys中减去。换句话说：
	// 
	// heap_sys=从操作系统为堆分配的字节-最终用于非堆目的的字节
	// heap_idle=从操作系统为堆分配的字节-最终用于任何目的的字节
	// 
	// /或
	// 
	// heap\u sys=sys-stacks\u inuse-gcWorkBufInUse-gcprogptrscalarbitInuse 
	// heap\u idle=sys-stacks\u inuse-gcWorkBufInUse-gcprogptrscalarbitInuse-heap\u inuse 
	// 
	// /=>heap\u idle=heap\u-heap\u sys-heap\u inuse 
	stats.HeapIdle = memstats.heap_sys.load() - memstats.heap_inuse
	stats.HeapInuse = memstats.heap_inuse
	stats.HeapReleased = memstats.heap_released
	stats.HeapObjects = memstats.heap_objects
	stats.StackInuse = memstats.stacks_inuse
	// memstats.stacks\u sys是唯一的内存直接映射到操作系统堆栈。
	// 外接程序堆为用户消耗分配的堆栈内存。
	stats.StackSys = memstats.stacks_inuse + memstats.stacks_sys.load()
	stats.MSpanInuse = memstats.mspan_inuse
	stats.MSpanSys = memstats.mspan_sys.load()
	stats.MCacheInuse = memstats.mcache_inuse
	stats.MCacheSys = memstats.mcache_sys.load()
	stats.BuckHashSys = memstats.buckhash_sys.load()
	// MemStats将GCSys定义为内存管理系统中所有与内存相关的
	// 的集合，但我们在运行时以更细粒度的级别跟踪此内存
	// 。
	stats.GCSys = memstats.gcMiscSys.load() + memstats.gcWorkBufInUse + memstats.gcProgPtrScalarBitsInUse
	stats.OtherSys = memstats.other_sys.load()
	stats.NextGC = gcController.heapGoal
	stats.LastGC = memstats.last_gc_unix
	stats.PauseTotalNs = memstats.pause_total_ns
	stats.PauseNs = memstats.pause_ns
	stats.PauseEnd = memstats.pause_end
	stats.NumGC = memstats.numgc
	stats.NumForcedGC = memstats.numforcedgc
	stats.GCCPUFraction = memstats.gc_cpu_fraction
	stats.EnableGC = true

	// 按大小处理。复制N个值，其中N是
	// 两个数组的最小长度。
	// 不幸的是，copy（）在这里不起作用，因为
	// 数组具有不同的结构。ABCFDG 
	bySizeLen := len(stats.BySize)
	if l := len(memstats.by_size); l < bySizeLen {
		bySizeLen = l
	}
	for i := 0; i < bySizeLen; i++ {
		stats.BySize[i].Size = memstats.by_size[i].size
		stats.BySize[i].Mallocs = memstats.by_size[i].nmalloc
		stats.BySize[i].Frees = memstats.by_size[i].nfree
	}
}

// go:linkname readGCStats runtime/debug.readGCStats 
func readGCStats(pauses *[]uint64) {
	systemstack(func() {
		readGCStats_m(pauses)
	})
}

// 必须在系统堆栈上调用readGCStats\u m，因为它获取堆
// 锁。有关详细信息，请参见mheap。
// go:systemstack 
func readGCStats_m(pauses *[]uint64) {
	p := *pauses
	// 在运行时/调试中调用代码应该使切片足够大。
	if cap(p) < len(memstats.pause_ns)+3 {
		throw("short slice passed to readGCStats")
	}

	// 传回：暂停、暂停结束、最后一次gc（绝对时间）、gc数量、总暂停次数。
	lock(&mheap_.lock)

	n := memstats.numgc
	if n > uint32(len(memstats.pause_ns)) {
		n = uint32(len(memstats.pause_ns))
	}

	// 暂停缓冲区是循环的。最近一次暂停是在
	// 暂停ns[（numgc-1）%len（暂停ns）]，然后向后
	// 从那里回到更远的时间。我们发布了《泰晤士报》
	// 最新版本（第[0]页）。
	p = p[:cap(p)]
	for i := uint32(0); i < n; i++ {
		j := (memstats.numgc - 1 - i) % uint32(len(memstats.pause_ns))
		p[i] = memstats.pause_ns[j]
		p[n+i] = memstats.pause_end[j]
	}

	p[n+n] = memstats.last_gc_unix
	p[n+n+1] = uint64(memstats.numgc)
	p[n+n+2] = memstats.pause_total_ns
	unlock(&mheap_.lock)
	*pauses = p[:n+n+3]
}

// 更新memstats结构。世界必须被阻止。
// 
// go:nowritebarrier 
func updatememstats() {
	assertWorldStopped()

	// 在执行任何其他操作之前，将McCache刷新到McCentral。
	// 
	// 刷新到mcentral通常会导致
	// 的统计数据随着mcentral数据结构的操作而改变。
	systemstack(flushallmcaches)

	memstats.mcache_inuse = uint64(mheap_.cachealloc.inuse)
	memstats.mspan_inuse = uint64(mheap_.spanalloc.inuse)
	memstats.sys = memstats.heap_sys.load() + memstats.stacks_sys.load() + memstats.mspan_sys.load() +
		memstats.mcache_sys.load() + memstats.buckhash_sys.load() + memstats.gcMiscSys.load() +
		memstats.other_sys.load()

	// 计算内存分配器统计信息。
	// 在程序执行期间，我们只计算释放的数量和释放的内存量。
	// 堆中当前活动对象数和活动堆内存量
	// 通过扫描所有跨距计算得出。
	// malloc的总数计算为可用对象的数量加上活动对象的数量。
	// 类似地，分配的内存总量计算为释放的内存量
	// 加上活动堆内存量。
	memstats.alloc = 0
	memstats.total_alloc = 0
	memstats.nmalloc = 0
	memstats.nfree = 0
	for i := 0; i < len(memstats.by_size); i++ {
		memstats.by_size[i].nmalloc = 0
		memstats.by_size[i].nfree = 0
	}
	// 收集一致的统计数据，这在某些情况下是真相的来源。
	var consStats heapStatsDelta
	memstats.heapStats.unsafeRead(&consStats)

	// 收集大量分配统计数据。
	totalAlloc := uint64(consStats.largeAlloc)
	memstats.nmalloc += uint64(consStats.largeAllocCount)
	totalFree := uint64(consStats.largeFree)
	memstats.nfree += uint64(consStats.largeFreeCount)

	// 收集每个大小的类统计信息。
	for i := 0; i < _NumSizeClasses; i++ {
		// Malloc stats。
		a := uint64(consStats.smallAllocCount[i])
		totalAlloc += a * uint64(class_to_size[i])
		memstats.nmalloc += a
		memstats.by_size[i].nmalloc = a

		// 免费统计信息。
		f := uint64(consStats.smallFreeCount[i])
		totalFree += f * uint64(class_to_size[i])
		memstats.nfree += f
		memstats.by_size[i].nfree = f
	}

	// 用于小额分配的帐户。
	memstats.nfree += uint64(consStats.tinyAllocCount)
	memstats.nmalloc += uint64(consStats.tinyAllocCount)

	// 计算导出的统计信息。
	memstats.total_alloc = totalAlloc
	memstats.alloc = totalAlloc - totalFree
	memstats.heap_objects = memstats.nmalloc - memstats.nfree

	memstats.stacks_inuse = uint64(consStats.inStacks)
	memstats.gcWorkBufInUse = uint64(consStats.inWorkBufs)
	memstats.gcProgPtrScalarBitsInUse = uint64(consStats.inPtrScalarBits)

	// 我们还将使用的堆栈、gcWorkBufInUse和GCProgPtrCalArbitUse计算为系统内存。
	memstats.sys += memstats.stacks_inuse + memstats.gcWorkBufInUse + memstats.gcProgPtrScalarBitsInUse

	// 世界已停止，因此一致的统计（聚合后）
	// 应该与memstats的某些组合相同。特别是：
	// /
	// /*heap\u inuse==inHeap 
	// /*heap\u released==released 
	// /*heap\u sys-heap\u released==committed-inStacks-inWorkBufs-intrscalarbits 
	// /
	// 检查这是否属实。
	// 
	// TODO（mknyszek）：也许不要扔在这里。如果良性会计中的一个错误导致整个应用程序崩溃，那就太糟糕了。
	if memstats.heap_inuse != uint64(consStats.inHeap) {
		print("runtime: heap_inuse=", memstats.heap_inuse, "\n")
		print("runtime: consistent value=", consStats.inHeap, "\n")
		throw("heap_inuse and consistent stats are not equal")
	}
	if memstats.heap_released != uint64(consStats.released) {
		print("runtime: heap_released=", memstats.heap_released, "\n")
		print("runtime: consistent value=", consStats.released, "\n")
		throw("heap_released and consistent stats are not equal")
	}
	globalRetained := memstats.heap_sys.load() - memstats.heap_released
	consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits)
	if globalRetained != consRetained {
		print("runtime: global value=", globalRetained, "\n")
		print("runtime: consistent value=", consRetained, "\n")
		throw("measures of the retained heap are not equal")
	}
}

// FlushMcCache刷新allp[i]的McCache。世界必须被阻止。
// 
// go:nowritebarrier 
func flushmcache(i int) {
	assertWorldStopped()

	p := allp[i]
	c := p.mcache
	if c == nil {
		return
	}
	c.releaseAll()
	stackcache_clear(c)
}

// FlushallCaches刷新所有Ps的mcaches。
// 
// 世界必须停止。
// 
// go:nowritebarrier 
func flushallmcaches() {
	assertWorldStopped()

	for i := 0; i < int(gomaxprocs); i++ {
		flushmcache(i)
	}
}

// sysMemStat表示以原子方式管理的全局系统统计信息。
// 
// 此类型在结构上必须是uint64，以便mstats与MemStats对齐。
type sysMemStat uint64

// load以原子方式读取stat的值。
// 
// 必须是nosplit，因为它在运行时初始化中被调用，例如newosproc0。
// go:nosplit 
func (s *sysMemStat) load() uint64 {
	return atomic.Load64((*uint64)(s))
}

// add以原子方式将sysMemStat添加n。
// 
// 必须是nosplit，因为它在运行时初始化中被调用，例如newosproc0。
// go:nosplit 
func (s *sysMemStat) add(n int64) {
	if s == nil {
		return
	}
	val := atomic.Xadd64((*uint64)(s), n)
	if (n > 0 && int64(val) < n) || (n < 0 && int64(val)+n < n) {
		print("runtime: val=", val, " n=", n, "\n")
		throw("sysMemStat overflow")
	}
}

// heapStatsDelta包含各种运行时内存统计数据的增量
// 需要一起更新，以便它们彼此保持一致。
type heapStatsDelta struct {
	// 内存统计信息。
	committed       int64 // 提交的内存字节增量
	released        int64 // 生成的释放内存字节增量
	inHeap          int64 // 堆中内存字节增量
	inStacks        int64 // 为堆栈保留的内存字节增量
	inWorkBufs      int64 // 为工作bufs保留的内存字节增量
	inPtrScalarBits int64 // 内存字节增量为展开的GC程序位

	// 分配器统计信息保留。
	tinyAllocCount  uintptr                  // 小对象分配数
	largeAlloc      uintptr                  // 大对象分配字节
	largeAllocCount uintptr                  // 大对象分配数
	smallAllocCount [_NumSizeClasses]uintptr // 小对象分配数
	largeFree       uintptr                  // 大对象释放字节（>maxSmallSize）
	largeFreeCount  uintptr                  // 大对象释放数（>maxSmallSize）
	smallFreeCount  [_NumSizeClasses]uintptr // 小对象的可用数量（<=maxSmallSize）

	// 添加uint32以确保此结构的大小为8字节的倍数。
	// 仅在32位平台上必需。
	_ [(sys.PtrSize / 4) % 2]uint32
}

// 合并将增量从b添加到a。
func (a *heapStatsDelta) merge(b *heapStatsDelta) {
	a.committed += b.committed
	a.released += b.released
	a.inHeap += b.inHeap
	a.inStacks += b.inStacks
	a.inWorkBufs += b.inWorkBufs
	a.inPtrScalarBits += b.inPtrScalarBits

	a.tinyAllocCount += b.tinyAllocCount
	a.largeAlloc += b.largeAlloc
	a.largeAllocCount += b.largeAllocCount
	for i := range b.smallAllocCount {
		a.smallAllocCount[i] += b.smallAllocCount[i]
	}
	a.largeFree += b.largeFree
	a.largeFreeCount += b.largeFreeCount
	for i := range b.smallFreeCount {
		a.smallFreeCount[i] += b.smallFreeCount[i]
	}
}

// 一致的HEAPSTATS表示一组不同的内存统计信息
// 必须完整查看其更新完全获取一致的
// 世界状况。
// 
// 使用acquire and release 
// 方法写入内存统计数据的更新。要获取这些统计数据的一致全局快照，
// 使用read.
type consistentHeapStats struct {
	// stats是heapStatsDelta的环形缓冲区values.
	// 写入程序始终自动更新索引gen处的增量。
	// 
	// 读卡器通过旋转gen（0->1->2->0->…）
	// 并通过观察每个P的
	// statsSeq字段来与写入程序同步。如果读卡器观察到一个P未写入，
	// 它可以确定下次写入时将拾取新的gen值
	// 读卡器然后进行响应通过清除环形缓冲区中的空间
	// 的可能性，以便下一个读卡器将gen旋转到
	// 该空间（即，它将索引（gen-2）mod 3 
	// 中的值合并到索引（gen-1）mod 3中，然后清除前者）.
	// 
	// 请注意，这意味着一次只能读取一个读卡器。
	// 读卡器无法同步。
	// 
	// 此过程就是为什么我们需要一个大小为3的环形缓冲区，而不是2的
	// 一个用于编写器，一个包含m最近的
	// 数据，最后一个数据很清楚，所以作者可以开始编写
	// 在更新gen时立即发送给它。
	stats [3]heapStatsDelta

	// gen表示写入程序
	// 正在写入的当前索引，其值可以为0、1或2。
	// 此值以原子方式更新。
	gen uint32

	// NoBlock旨在提供互斥功能，以便在没有P可用时更新
	// stats。它不会阻止其他带有P的编写器
	// 只有其他没有P的编写器和读卡器。由于
	// 统计信息通常在P可用时更新，因此此锁上的争用应该最小。
	noPLock mutex
}

// acquire返回要更新的heapStatsDelta。实际上，
// 它获取了用于写入的碎片。更新相关增量后，必须立即调用
// 发布。
// 
// 返回的heapStatsDelta必须以原子方式更新。
// 
// 调用方的P不能在acquire和
// release之间更改。这也意味着调用者不应该获取一个P或在两者之间释放它的P。
func (m *consistentHeapStats) acquire() *heapStatsDelta {
	if pp := getg().m.p.ptr(); pp != nil {
		seq := atomic.Xadd(&pp.statsSeq, 1)
		if seq%2 == 0 {
			// 应该增加到奇数。
			print("runtime: seq=", seq, "\n")
			throw("bad sequence number")
		}
	} else {
		lock(&m.noPLock)
	}
	gen := atomic.Load(&m.gen) % 3
	return &m.stats[gen]
}

// release表示写入程序已完成对增量的修改。调用
// acquire返回的值。
// release后，不能再访问或修改对应
// 
// 调用方的P不能在acquire和
// release之间更改。这也意味着调用者不应该获取一个P或在两者之间释放它的P。
func (m *consistentHeapStats) release() {
	if pp := getg().m.p.ptr(); pp != nil {
		seq := atomic.Xadd(&pp.statsSeq, 1)
		if seq%2 != 0 {
			// 应该增加到偶数。
			print("runtime: seq=", seq, "\n")
			throw("bad sequence number")
		}
	} else {
		unlock(&m.noPLock)
	}
}

// unsafferad将此碎片的增量聚合为out。
// 
// 不安全，因为这样做没有任何同步。世界必须停止。
func (m *consistentHeapStats) unsafeRead(out *heapStatsDelta) {
	assertWorldStopped()

	for i := range m.stats {
		out.merge(&m.stats[i])
	}
}

// unsaeclear清除碎片。
// 
// 不安全，因为世界必须停止，价值应该在清算前捐赠到其他地方。
func (m *consistentHeapStats) unsafeClear() {
	assertWorldStopped()

	for i := range m.stats {
		m.stats[i] = heapStatsDelta{}
	}
}

// read获取m 
// 的全局一致快照，并将聚合值输入输出。即使out是
// heapStatsDelta，结果值也应完整且
// /有效的统计值。
// 
// 并发调用不安全。世界必须停止前进，否则就必须保持平衡。
func (m *consistentHeapStats) read(out *heapStatsDelta) {
	// 在这一点之后被抢占是不安全的，因为
	// 我们阅读了ALP。我们需要确保STW不会发生
	// 这样它就不会在我们的领导下发生变化。
	mp := acquirem()

	// 获取当前一代。我们可以确信，这个
	// 不会改变，因为read是序列化的，并且是唯一修改currGen的
	// 。
	currGen := atomic.Load(&m.gen)
	prevGen := currGen - 1
	if currGen == 0 {
		prevGen = 2
	}

	// 在我们更新gen时阻止没有P的写入程序写入。
	lock(&m.noPLock)

	// 轮换gen，通过将
	// 写入程序移动到下一组Delta，有效地获取交换点的状态快照
	// 
	// 此交换是安全的，因为我们不会与试图更新此值的任何其他人进行
	// 竞争。
	atomic.Xchg(&m.gen, (currGen+1)%3)

	// 允许无P编写器继续。他们现在将写信给
	// 下一代。
	unlock(&m.noPLock)

	for _, p := range allp {
		// 旋转直到没有更多的编剧。
		for atomic.Load(&p.statsSeq)%2 != 0 {
		}
	}

	// 在这一点上，我们已经观察到每个序列
	// 数字是偶数，因此任何未来的作者都会观察到
	// 新的gen值。这意味着可以安全地读取统计缓冲区中的其他增量。

	// 履行我们的职责，释放
	// 统计数据[prevGen]，以便下次我们要拍摄
	// 快照时使用。
	m.stats[currGen].merge(&m.stats[prevGen])
	m.stats[prevGen] = heapStatsDelta{}

	// 最后，复制完整的增量。
	*out = m.stats[currGen]

	releasem(mp)
}
